from django.shortcuts import render
from rest_framework import viewsets, status
from rest_framework.response import Response
from rest_framework.decorators import action
from rest_framework.parsers import MultiPartParser, FormParser, JSONParser
from rest_framework.permissions import AllowAny, IsAuthenticated
from django.shortcuts import get_object_or_404
from django.http import FileResponse, Http404
from .models import Document, Question
from .serializers import DocumentSerializer, QuestionSerializer, DocumentDetailSerializer
import os
import mimetypes
from django.conf import settings
from django.utils.encoding import escape_uri_path
import logging
import re
import fitz  # PyMuPDF for PDF processing
import docx  # python-docx for DOCX processing
from django.core.files.storage import default_storage
from rest_framework.views import APIView
from django.db.models import Sum, Count
from collections import defaultdict

logger = logging.getLogger(__name__)

# 设置最大文件大小为2GB
MAX_FILE_SIZE = 2 * 1024 * 1024 * 1024  # 2GB in bytes

# Create your views here.

class DocumentViewSet(viewsets.ModelViewSet):
    queryset = Document.objects.all()
    serializer_class = DocumentSerializer
    parser_classes = (MultiPartParser, FormParser, JSONParser)
    
    def get_serializer_class(self):
        """根据请求类型返回不同的序列化器"""
        if self.action == 'retrieve':
            return DocumentDetailSerializer
        return self.serializer_class
    
    def get_permissions(self):
        """
        根据不同的操作返回不同的权限
        """
        if self.action in ['list', 'retrieve', 'download', 'shared', 'shared_detail']:
            permission_classes = [AllowAny]
        else:
            permission_classes = [IsAuthenticated]
        return [permission() for permission in permission_classes]
    
    def create(self, request, *args, **kwargs):
        """创建文档时检查文件大小"""
        try:
            file_obj = request.FILES.get('file')
            if not file_obj:
                return Response(
                    {'error': '未提供文件'},
                    status=status.HTTP_400_BAD_REQUEST
                )
            
            # 检查文件大小
            if file_obj.size > MAX_FILE_SIZE:
                logger.warning(f"文件大小超过限制: {file_obj.size} bytes > {MAX_FILE_SIZE} bytes")
                return Response(
                    {'error': f'文件大小不能超过2GB，当前文件大小为 {file_obj.size / (1024*1024*1024):.2f}GB'},
                    status=status.HTTP_400_BAD_REQUEST
                )
            
            # 创建文档
            serializer = self.get_serializer(data=request.data)
            serializer.is_valid(raise_exception=True)
            self.perform_create(serializer)
            headers = self.get_success_headers(serializer.data)
            
            logger.info(f"文档上传成功: ID={serializer.instance.id}, 标题={serializer.instance.title}, 大小={file_obj.size / (1024*1024):.2f}MB")
            return Response(
                serializer.data,
                status=status.HTTP_201_CREATED,
                headers=headers
            )
            
        except Exception as e:
            logger.error(f"文件上传失败: {str(e)}")
            return Response(
                {'error': f'上传失败: {str(e)}'},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )
    
    def perform_create(self, serializer):
        """创建文档时记录上传者IP"""
        try:
            x_forwarded_for = self.request.META.get('HTTP_X_FORWARDED_FOR')
            if x_forwarded_for:
                ip = x_forwarded_for.split(',')[0]
            else:
                ip = self.request.META.get('REMOTE_ADDR')
            instance = serializer.save(upload_ip=ip)
            logger.info(f"文档上传成功: ID={instance.id}, 标题={instance.title}, IP={ip}")
        except Exception as e:
            logger.error(f"文档上传失败: {str(e)}")
            raise
    
    @action(detail=True, methods=['get'])
    def download(self, request, pk=None):
        """下载文件并增加下载计数"""
        try:
            document = self.get_object()
            logger.info(f"尝试下载文档: ID={document.id}, 标题={document.title}")
            
            if not document.file:
                logger.warning(f"文档 {document.id} 没有关联文件")
                return Response(
                    {'error': '文件不存在或已被删除'},
                    status=status.HTTP_404_NOT_FOUND
                )
            
            # 获取文件的完整路径
            file_path = document.file.path
            logger.info(f"文件路径: {file_path}")
            
            if not os.path.exists(file_path):
                logger.warning(f"文件不存在: {file_path}")
                return Response(
                    {'error': '文件不存在或已被删除'},
                    status=status.HTTP_404_NOT_FOUND
                )
            
            try:
                # 增加下载计数
                document.download_count += 1
                document.save(update_fields=['download_count'])
                
                # 获取文件的MIME类型
                content_type, _ = mimetypes.guess_type(file_path)
                if not content_type:
                    content_type = 'application/octet-stream'
                
                # 构建文件响应
                response = FileResponse(
                    open(file_path, 'rb'),
                    content_type=content_type
                )
                
                # 设置文件名，处理中文文件名
                filename = os.path.basename(document.file.name)
                encoded_filename = escape_uri_path(filename)
                
                response['Content-Disposition'] = f'attachment; filename="{encoded_filename}"'
                response['Access-Control-Expose-Headers'] = 'Content-Disposition'
                
                logger.info(f"文档 {document.id} 下载成功")
                return response
                
            except IOError as e:
                logger.error(f"文件读取错误: {str(e)}")
                return Response(
                    {'error': '文件读取失败'},
                    status=status.HTTP_500_INTERNAL_SERVER_ERROR
                )
                
        except Http404:
            logger.warning(f"文档不存在: pk={pk}")
            return Response(
                {'error': '文档不存在'},
                status=status.HTTP_404_NOT_FOUND
            )
        except Exception as e:
            logger.error(f"下载错误: {str(e)}")
            return Response(
                {'error': f'下载失败: {str(e)}'},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )
    
    def destroy(self, request, *args, **kwargs):
        """删除文档及其对应的文件"""
        try:
            document = self.get_object()
            logger.info(f"尝试删除文档: ID={document.id}, 标题={document.title}")
            
            if document.file:
                file_path = document.file.path
                if os.path.isfile(file_path):
                    os.remove(file_path)
                    logger.info(f"文件已删除: {file_path}")
            
            document.delete()
            logger.info(f"文档记录已删除: ID={document.id}")
            
            return Response(
                {'message': '文档删除成功'},
                status=status.HTTP_204_NO_CONTENT
            )
        except Exception as e:
            logger.error(f"删除错误: {str(e)}")
            return Response(
                {'error': f'删除失败: {str(e)}'},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )
    
    @action(detail=True, methods=['post'])
    def extract_questions(self, request, pk=None):
        """提取文档中的题目"""
        try:
            document = self.get_object()
            logger.info(f"开始提取文档题目: ID={document.id}, 标题={document.title}")
            
            if not document.file:
                return Response(
                    {'error': '文件不存在或已被删除'},
                    status=status.HTTP_404_NOT_FOUND
                )
            
            # 获取文件的完整路径
            file_path = document.file.path
            
            if not os.path.exists(file_path):
                return Response(
                    {'error': '文件不存在或已被删除'},
                    status=status.HTTP_404_NOT_FOUND
                )
            
            # 根据文件类型调用不同的处理函数
            file_ext = document.file_type.lower()
            
            if file_ext in ['pdf']:
                questions = self._extract_questions_from_pdf(document, file_path)
            elif file_ext in ['doc', 'docx']:
                questions = self._extract_questions_from_docx(document, file_path)
            else:
                return Response(
                    {'error': f'不支持的文件类型: {file_ext}，仅支持PDF和DOCX文件'},
                    status=status.HTTP_400_BAD_REQUEST
                )
            
            return Response(
                {
                    'message': f'成功提取 {len(questions)} 个题目',
                    'questions_count': len(questions)
                },
                status=status.HTTP_200_OK
            )
            
        except Exception as e:
            logger.error(f"题目提取错误: {str(e)}")
            return Response(
                {'error': f'题目提取失败: {str(e)}'},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )
    
    def _extract_questions_from_pdf(self, document, file_path):
        """从PDF文件中提取题目"""
        # 删除文档先前的题目
        document.questions.all().delete()
        questions = []
        
        # 打开PDF文件
        pdf_document = fitz.open(file_path)
        
        current_question = ""
        current_answer = ""
        in_answer = False
        current_page = 1
        
        # 题目识别的正则表达式
        question_patterns = [
            r'^\d+\.\s+.+',  # 数字开头：1. 题目内容
            r'^[一二三四五六七八九十]+、\s*.+',  # 中文数字开头：一、题目内容
            r'^第[一二三四五六七八九十]+题\s*.+',  # "第X题"开头
            r'^[（\(]\s*\d+\s*[）\)]\s*.+'  # 带括号的数字：(1) 题目内容
        ]
        
        for page_num in range(len(pdf_document)):
            page = pdf_document[page_num]
            text = page.get_text()
            lines = text.split('\n')
            
            for line in lines:
                line = line.strip()
                if not line:
                    continue
                
                # 检查是否是新题目
                is_new_question = any(re.match(pattern, line) for pattern in question_patterns)
                
                # 如果遇到新题目，保存先前的题目
                if is_new_question and current_question:
                    # 创建题目对象
                    question = Question.objects.create(
                        document=document,
                        content=current_question.strip(),
                        answer=current_answer.strip() if current_answer else None,
                        page_number=current_page,
                        question_type='未分类'
                    )
                    questions.append(question)
                    
                    # 重置当前题目和答案
                    current_question = line
                    current_answer = ""
                    in_answer = False
                    current_page = page_num + 1
                elif is_new_question:
                    # 第一个题目
                    current_question = line
                    current_page = page_num + 1
                elif line.startswith("答案:") or line.startswith("解答:"):
                    # 进入答案部分
                    in_answer = True
                    current_answer += line[3:].strip() + " "
                elif in_answer:
                    # 继续添加到答案
                    current_answer += line + " "
                else:
                    # 继续添加到题目
                    current_question += " " + line
        
        # 处理最后一个题目
        if current_question:
            question = Question.objects.create(
                document=document,
                content=current_question.strip(),
                answer=current_answer.strip() if current_answer else None,
                page_number=current_page,
                question_type='未分类'
            )
            questions.append(question)
        
        return questions
    
    def _extract_questions_from_docx(self, document, file_path):
        """从DOCX文件中提取题目"""
        # 删除文档先前的题目
        document.questions.all().delete()
        questions = []
        
        # 打开DOCX文件
        doc = docx.Document(file_path)
        
        current_question = ""
        current_answer = ""
        in_answer = False
        current_page = 1  # DOCX没有页码概念，默认为1
        
        # 题目识别的正则表达式
        question_patterns = [
            r'^\d+\.\s+.+',  # 数字开头：1. 题目内容
            r'^[一二三四五六七八九十]+、\s*.+',  # 中文数字开头：一、题目内容
            r'^第[一二三四五六七八九十]+题\s*.+',  # "第X题"开头
            r'^[（\(]\s*\d+\s*[）\)]\s*.+'  # 带括号的数字：(1) 题目内容
        ]
        
        for para in doc.paragraphs:
            line = para.text.strip()
            if not line:
                continue
            
            # 检查是否是新题目
            is_new_question = any(re.match(pattern, line) for pattern in question_patterns)
            
            # 如果遇到新题目，保存先前的题目
            if is_new_question and current_question:
                # 创建题目对象
                question = Question.objects.create(
                    document=document,
                    content=current_question.strip(),
                    answer=current_answer.strip() if current_answer else None,
                    page_number=current_page,
                    question_type='未分类'
                )
                questions.append(question)
                
                # 重置当前题目和答案
                current_question = line
                current_answer = ""
                in_answer = False
            elif is_new_question:
                # 第一个题目
                current_question = line
            elif line.startswith("答案:") or line.startswith("解答:"):
                # 进入答案部分
                in_answer = True
                current_answer += line[3:].strip() + " "
            elif in_answer:
                # 继续添加到答案
                current_answer += line + " "
            else:
                # 继续添加到题目
                current_question += " " + line
        
        # 处理最后一个题目
        if current_question:
            question = Question.objects.create(
                document=document,
                content=current_question.strip(),
                answer=current_answer.strip() if current_answer else None,
                page_number=current_page,
                question_type='未分类'
            )
            questions.append(question)
        
        return questions

    @action(detail=False, methods=['get'])
    def shared(self, request):
        """获取共享文档列表"""
        try:
            # 获取共享文档
            # 假设Document模型有is_shared字段，如果没有，需要根据实际模型调整查询条件
            shared_documents = Document.objects.filter(is_shared=True)
            
            # 分页处理
            page = self.paginate_queryset(shared_documents)
            if page is not None:
                serializer = self.get_serializer(page, many=True)
                return self.get_paginated_response(serializer.data)
            
            # 序列化文档
            serializer = self.get_serializer(shared_documents, many=True)
            
            return Response({
                'success': True,
                'count': shared_documents.count(),
                'results': serializer.data
            })
            
        except Exception as e:
            logger.error(f"获取共享文档错误: {str(e)}")
            return Response(
                {'success': False, 'message': f'获取共享文档失败: {str(e)}'},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )
    
    @action(detail=True, methods=['get'])
    def shared_detail(self, request, pk=None):
        """获取单个共享文档的详细信息"""
        try:
            document = self.get_object()
            
            # 检查文档是否共享
            if not document.is_shared:
                return Response(
                    {'success': False, 'message': '该文档未共享'},
                    status=status.HTTP_403_FORBIDDEN
                )
            
            # 序列化文档
            serializer = DocumentDetailSerializer(document)
            
            # 获取文档相关的题目数量
            questions_count = Question.objects.filter(document=document).count()
            
            return Response({
                'success': True,
                'document': serializer.data,
                'questions_count': questions_count
            })
            
        except Exception as e:
            logger.error(f"获取共享文档详情错误: {str(e)}")
            return Response(
                {'success': False, 'message': f'获取共享文档详情失败: {str(e)}'},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )

class QuestionViewSet(viewsets.ModelViewSet):
    queryset = Question.objects.all()
    serializer_class = QuestionSerializer
    permission_classes = [IsAuthenticated]
    
    def get_queryset(self):
        """按文档和题目类型筛选题目"""
        queryset = Question.objects.all()
        document_id = self.request.query_params.get('document')
        if document_id:
            queryset = queryset.filter(document_id=document_id)
        question_type = self.request.query_params.get('question_type')
        if question_type:
            queryset = queryset.filter(question_type=question_type)
        return queryset

class DashboardStatsView(APIView):
    """仪表盘统计数据视图"""
    permission_classes = [AllowAny]
    
    def get(self, request, format=None):
        """获取仪表盘的统计数据"""
        try:
            # 文档总数
            total_documents = Document.objects.count()
            
            # 总下载次数
            total_downloads = Document.objects.aggregate(total=Sum('download_count'))['total'] or 0
            
            # 文件总大小
            total_size_bytes = Document.objects.aggregate(total=Sum('file_size'))['total'] or 0
            # 格式化文件大小
            if total_size_bytes < 1024:
                total_size = f"{total_size_bytes} B"
            elif total_size_bytes < 1024 * 1024:
                total_size = f"{total_size_bytes / 1024:.2f} KB"
            elif total_size_bytes < 1024 * 1024 * 1024:
                total_size = f"{total_size_bytes / (1024 * 1024):.2f} MB"
            else:
                total_size = f"{total_size_bytes / (1024 * 1024 * 1024):.2f} GB"
            
            # 文件类型统计
            file_types = Document.objects.values('file_type').annotate(count=Count('id'))
            file_type_stats = {item['file_type']: item['count'] for item in file_types}
            file_types_count = len(file_type_stats)
            
            # 构建返回数据
            data = {
                'totalDocuments': total_documents,
                'totalDownloads': total_downloads,
                'totalSize': total_size,
                'fileTypes': file_types_count,
                'fileTypeDistribution': file_type_stats
            }
            
            return Response(data)
            
        except Exception as e:
            logger.error(f"获取统计数据失败: {str(e)}")
            return Response(
                {'error': f'获取统计数据失败: {str(e)}'},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )

class ActivityView(APIView):
    """最近活动视图"""
    permission_classes = [AllowAny]
    
    def get(self, request, format=None):
        """获取最近活动数据"""
        try:
            # 获取最近上传的文档
            recent_documents = Document.objects.order_by('-upload_time')[:5]
            
            activities = []
            for doc in recent_documents:
                # 根据文件类型选择图标类型
                icon_type = 'primary'  # 默认为蓝色
                if doc.file_type.lower() in ['pdf', 'txt']:
                    icon_type = 'danger'  # PDF和TXT为红色
                elif doc.file_type.lower() in ['doc', 'docx']:
                    icon_type = 'primary'  # Word为蓝色
                elif doc.file_type.lower() in ['xls', 'xlsx']:
                    icon_type = 'success'  # Excel为绿色
                elif doc.file_type.lower() in ['ppt', 'pptx']:
                    icon_type = 'warning'  # PPT为黄色
                
                # 格式化上传时间
                upload_time = doc.upload_time.strftime('%Y-%m-%d %H:%M')
                
                activities.append({
                    'id': doc.id,
                    'content': f'上传了新文档 "{doc.title}.{doc.file_type.lower()}"',
                    'time': upload_time,
                    'type': icon_type
                })
            
            return Response(activities)
            
        except Exception as e:
            logger.error(f"获取最近活动失败: {str(e)}")
            return Response(
                {'error': f'获取最近活动失败: {str(e)}'},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )
